

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

/// 包装 ChangeNotifierProvider + Consumer，提供一种即创建，即消费的情景
/// 另外，如果使用 ChangeNotifierProvider 的 create 方法，来创建 Provider 的话，
/// 那么当 ChangeNotifierProvider 从控件树上移除的时候，会自动调用（观察者如 ChangeNotifier 的） dispose 方法，
/// 这对于释放内存、取消网络、防止异步未完成页面销毁而出错的预防很有帮助，所以建议使用 create 方法
/// 如果，ChangeNotifierProvider.value 方法创建的话，则不会自动调用 dispose
class BLProviderWidget<T extends ChangeNotifier> extends StatefulWidget {

  /// 要提供的监听变化的数据 (大多数情况下是： SomeViewModel extends ChangeNotifier)
  final T notifierModel;
  /// Consumer 中要创建的 Widget，具体要创建什么样的 UI，通过 build 自行处理
  final Widget Function(BuildContext context, T value, Widget child) consumerBuilder;
  /// 当 child 不需要因为状态改变为改变时，可以 hold 住，使其只创建一次
  final Widget holdChild;
  /// 当需要提前使用模型的时候（比如准备请求网络），可以实现这个方法
  final Function(T) onModelReady;


  const BLProviderWidget({Key key, @required this.notifierModel, @required this.consumerBuilder,  this.holdChild, this.onModelReady, }) : super(key: key);

  @override
  _BLProviderWidgetState<T> createState() => _BLProviderWidgetState<T>();
}

/// 包装 ChangeNotifierProvider + Consumer
class _BLProviderWidgetState <T extends ChangeNotifier> extends State<BLProviderWidget<T>> {

  T _notifierModel;

  @override
  void initState() {
    _notifierModel = widget.notifierModel;
    /// 拿到 ViewModel ，可以做一些预处理
    if (widget.onModelReady != null) {
      widget.onModelReady(_notifierModel);
    }
    super.initState();
  }

  @override
  Widget build(BuildContext context) {

    return ChangeNotifierProvider<T>(
      // 提供要监听的 Model
      create: (_) => _notifierModel,
      // builder 里 child 其实就是 ChangeNotifierProvider 的 child（这里没用到），
      // 为什么使用 build 而不直接使用 child：
      // builder 是对 child 调用了 Build Widget 的一层包装，Build 会提供一个新的 BuildContext，
      // 使得 child 和 ChangeNotifierProvider 有了父子级的关系（BuildContext），
      // 而我们使用的 Provider.of<Example>(context) 或 context.watch<Example> 或者 context.read<Example>
      // 都是通过 context 向父层级查询提供 Example 的 Provider
      // 而直接使用 child ，则不存在跨层级的，会触发 ProviderNotFoundException 异常，具体可以看 ProviderNotFoundException 的 toString 方法
      builder: (context, child){
        return Consumer<T>(
          builder: widget.consumerBuilder,
          child: widget.holdChild,
        );
      },
    );
  }
}


/// 创建 Provider 但延迟消费，消费时机，自己用 consumer 或者 of 等方法实现
class BLProviderLazy<T extends ChangeNotifier> extends StatefulWidget {

  /// 要提供的监听变化的数据 (大多数情况下是： SomeViewModel extends ChangeNotifier)
  final T notifierModel;
  /// child
  final Widget child;
  /// 当需要提前使用模型的时候（比如准备请求网络），可以实现这个方法
  final Function(T) onModelReady;


  const BLProviderLazy({Key key, @required this.notifierModel,  @required this.child, this.onModelReady, }) : super(key: key);

  @override
  _BLProviderLazyState<T> createState() => _BLProviderLazyState<T>();
}

/// 包装 ChangeNotifierProvider + Consumer
class _BLProviderLazyState <T extends ChangeNotifier> extends State<BLProviderLazy<T>> {

  T _notifierModel;

  @override
  void initState() {
    _notifierModel = widget.notifierModel;
    /// 拿到 ViewModel ，可以做一些预处理
    if (widget.onModelReady != null) {
      widget.onModelReady(_notifierModel);
    }
    super.initState();
  }

  @override
  Widget build(BuildContext context) {

    return ChangeNotifierProvider<T>(
      // 提供要监听的 Model
      create: (_) => _notifierModel,
      // builder 里 child 其实就是 ChangeNotifierProvider 的 child（这里没用到），
      // 为什么使用 build 而不直接使用 child：
      // builder 是对 child 调用了 Build Widget 的一层包装，Build 会提供一个新的 BuildContext，
      // 使得 child 和 ChangeNotifierProvider 有了父子级的关系（BuildContext），
      // 而我们使用的 Provider.of<Example>(context) 或 context.watch<Example> 或者 context.read<Example>
      // 都是通过 context 向父层级查询提供 Example 的 Provider
      // 而直接使用 child ，则不存在跨层级的，会触发 ProviderNotFoundException 异常，具体可以看 ProviderNotFoundException 的 toString 方法
      builder: (context, _){
        return widget.child;
      },
    );
  }
}

//------------------------------------------------------------------------------------

/// 同时提供两种观察数据
class BLProviderWidget2<A extends ChangeNotifier,  B extends ChangeNotifier> extends StatefulWidget {

  /// 要提供的监听变化的数据 1
  final A notifierModel1;
  /// 要提供的监听变化的数据 2
  final B notifierModel2;
  /// Consumer 中要创建的 Widget，具体要创建什么样的 UI，通过 build 自行处理
  final Widget Function(BuildContext context, A notifierModel1, B notifierModel2, Widget child) consumerBuilder;
  /// 当 child 不需要因为状态改变为改变时，可以 hold 住，使其只创建一次
  final Widget holdChild;
  /// 当需要提前使用模型的时候（比如准备请求网络），可以实现这个方法
  final Function(A, B) onModelReady;


  const BLProviderWidget2({Key key, @required this.notifierModel1, @required this.notifierModel2, @required this.consumerBuilder,  this.holdChild, this.onModelReady, }) : super(key: key);

  @override
  _BLProviderWidgetState2<A, B> createState() => _BLProviderWidgetState2<A, B>();
}

/// 包装 ChangeNotifierProvider + Consumer
class _BLProviderWidgetState2 <A extends ChangeNotifier,  B extends ChangeNotifier> extends State<BLProviderWidget2<A, B>> {

  /// 要提供的监听变化的数据 1
  A _notifierModel1;
  /// 要提供的监听变化的数据 2
  B _notifierModel2;

  @override
  void initState() {
    _notifierModel1 = widget.notifierModel1;
    _notifierModel2 = widget.notifierModel2;
    /// 拿到 ViewModel ，可以做一些预处理
    if (widget.onModelReady != null) {
      widget.onModelReady(_notifierModel1, _notifierModel2);
    }
    super.initState();
  }

  @override
  Widget build(BuildContext context) {

    return MultiProvider(
      providers: [
        ChangeNotifierProvider<A>(
          create: (_) => _notifierModel1,
        ),
        ChangeNotifierProvider<B>(
          create: (_) => _notifierModel2,
        ),
      ],
      child: Consumer2(
        builder: widget.consumerBuilder,
        child: widget.holdChild,
      ),
    );
  }
}
